package com.sync;

/**
 * 更优雅的方案
 * 如果第一次检查instance不为null，那么就不需要执行下面的加锁和初始化操作。因此，可以大幅降低synchronized带来的性能开销。
 * 双重检查锁定看起来似乎很完美，但这是一个错误的优化！为什么呢？
 * 在线程执行到第4行，代码读取到instance不为null时，instance引用的对象有可能还没有完成初始化。
 * 在第7行创建了一个对象，这行代码可以分解为如下的3行伪代码
  ---memory=allocate(); //1:分配对象的内存空间
  ---ctorInstance(memory); //2:初始化对象
  ---instance=memory; //3:设置instance指向刚分配的内存地址
 * 上面3行代码中的2和3之间，可能会被重排序（在一些JIT编译器上，这种重排序是真实发生的，如果不了解重排序，JMM会详细解释）。
 * 2和3之间重排序之后的执行时序如下
 ---memory=allocate(); //1:分配对象的内存空间
 ---instance=memory; //3:设置instance指向刚分配的内存地址，注意此时对象还没有被初始化
 ---ctorInstance(memory); //2:初始化对象
 回到示例代码第7行，如果发生重排序，另一个并发执行的线程B就有可能在第4行判断instance不为null。
 线程B接下来将访问instance所引用的对象，但此时这个对象可能还没有被A线程初始化。在知晓问题发生的根源之后，我们可以想出两个办法解决
 --不允许2和3重排序
 --允许2和3重排序，但不允许其他线程“看到”这个重排序
 介绍这两个解决方案的具体实现
 --- 基于volatile的解决方案--> 参考UnsafeLazyInitiallization04
 --- 基于类初始化的解决方案--> 参考InstanceFactory05
 *
 *
 *
 *
 * Created by 峥桂 on 2017/6/6.
 */
public class UnsafeLazyInitiallization03 {
    private static UnsafeLazyInitiallization03 instance;
    private UnsafeLazyInitiallization03() {}

    public synchronized  static UnsafeLazyInitiallization03 getInstance() {
        if (instance == null) { //4:第一次检查
            synchronized (UnsafeLazyInitiallization03.class) { //5:加锁
                if (instance == null) //6:第二次检查
                    instance = new UnsafeLazyInitiallization03(); //7:问题的根源出在这里
            } //8
        } //9
        return instance; //10

    }
}
